﻿Imports System.ComponentModel
Imports Microsoft.Win32
Imports iTextSharp.text.pdf
Imports System.IO
Imports ProgettoSicurezza.Client.ClientServices
Imports Org.BouncyCastle.Crypto.Parameters
Imports Org.BouncyCastle.X509

Public Class PDFControl
    Implements INotifyPropertyChanged

#Region "Presentation Properties"

    Private _Searching As Boolean = False
    Public Property Searching As Boolean
        Get
            Return _Searching
        End Get
        Set(value As Boolean)
            _Searching = value
            OnPropertyChanged("Searching")
            OnPropertyChanged("SearchProgressVisibility")
        End Set
    End Property

    Public ReadOnly Property SearchProgressVisibility As Visibility
        Get
            If Searching Then
                Return Windows.Visibility.Visible
            Else
                Return Windows.Visibility.Collapsed
            End If
        End Get
    End Property

    Public ReadOnly Property IsMyDocumentSelected As Boolean
        Get
            Return dgMyDocuments IsNot Nothing AndAlso dgMyDocuments.SelectedItem IsNot Nothing
        End Get
    End Property
    Public ReadOnly Property IsClientSelected As Boolean
        Get
            Return dgClients IsNot Nothing AndAlso dgClients.SelectedItem IsNot Nothing
        End Get
    End Property
    Public ReadOnly Property IsReadyToSend As Boolean
        Get
            Return IsMyDocumentSelected AndAlso IsClientSelected
        End Get
    End Property
    Public ReadOnly Property IsSignedDocumentSelected As Boolean
        Get
            Return dgDocuments.SelectedItem IsNot Nothing AndAlso DirectCast(dgDocuments.SelectedItem, PDFItem).IsSigned
        End Get
    End Property
    Public ReadOnly Property IsEncryptedDocumentSelected As Boolean
        Get
            Return dgDocuments.SelectedItem IsNot Nothing AndAlso DirectCast(dgDocuments.SelectedItem, PDFItem).IsEncrypted
        End Get
    End Property
    Public ReadOnly Property IsDocumentSelected As Boolean
        Get
            Return dgDocuments.SelectedItem IsNot Nothing
        End Get
    End Property
    Public ReadOnly Property CanSign As Boolean
        Get
            Return Repository.CurrentProfile IsNot Nothing AndAlso Repository.CurrentProfile.SigCert IsNot Nothing AndAlso Repository.CurrentProfile.SigCert.Certificate IsNot Nothing
        End Get
    End Property
    Public ReadOnly Property CanEncrypt As Boolean
        Get
            'Return Repository.CurrentProfile IsNot Nothing AndAlso Repository.CurrentProfile.CryptoCert IsNot Nothing AndAlso Repository.CurrentProfile.CryptoCert.Certificate IsNot Nothing
            Return True
        End Get
    End Property


#End Region

    Public Sub New()
        InitializeComponent()

        Dim myDocsView As New ListCollectionView(Repository.PDFDocuments)
        myDocsView.Filter = AddressOf MyDocumentsFilter

        Dim otherDocsView As New ListCollectionView(Repository.PDFDocuments)
        otherDocsView.Filter = AddressOf ReceivedDocumentsFilter

        dgClients.ItemsSource = Client2ClientHelper.FoundClients
        dgMyDocuments.ItemsSource = myDocsView
        dgDocuments.ItemsSource = otherDocsView
    End Sub

    Private Function MyDocumentsFilter(item As Object) As Boolean
        Dim pi As PDFItem = DirectCast(item, PDFItem)
        Return pi.Username = Repository.CurrentProfile.UserName
    End Function
    Private Function ReceivedDocumentsFilter(item As Object) As Boolean
        Dim pi As PDFItem = DirectCast(item, PDFItem)
        Return pi.Username <> Repository.CurrentProfile.UserName
    End Function


    Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
    Private Overloads Sub OnPropertyChanged(name As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
    End Sub

    Private Sub SearchCompleted()
        RemoveHandler Client2ClientHelper.SearchCompletedEvent, AddressOf SearchCompleted
        Searching = False
    End Sub

    Private Sub buttonSearch_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
        Searching = True
        AddHandler Client2ClientHelper.SearchCompletedEvent, AddressOf SearchCompleted
        Client2ClientHelper.AsyncDiscoverOtherClients()
    End Sub


    Private Sub AddDocument_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
        Dim ofd As New OpenFileDialog
        ofd.Filter = "PDF files (*.pdf)|*.pdf"
        Dim res = ofd.ShowDialog
        If res.HasValue AndAlso res.Value = True Then
            Dim item As PDFItem = PDFManager.CreateItem(ofd.FileName)
            Repository.PDFDocuments.Add(item)
        End If
    End Sub

    Private Sub DeleteDocument_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
        Dim item = TryCast(dgMyDocuments.SelectedItem, PDFItem)
        If item IsNot Nothing Then
            Repository.PDFDocuments.Remove(item)
        End If
    End Sub

    Private Sub MyDocuments_SelectionChanged(sender As System.Object, e As System.Windows.Controls.SelectionChangedEventArgs)
        OnPropertyChanged("IsMyDocumentSelected")
        OnPropertyChanged("IsReadyToSend")
    End Sub


    Private Sub Clients_SelectionChanged(sender As System.Object, e As System.Windows.Controls.SelectionChangedEventArgs)
        OnPropertyChanged("IsClientSelected")
        OnPropertyChanged("IsReadyToSend")
    End Sub

    Private Sub SignAndSend_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
        Dim client = DirectCast(dgClients.SelectedItem, DiscoveredClientItem)
        Dim item = DirectCast(dgMyDocuments.SelectedItem, PDFItem)
        Dim signedCopy = PDFManager.SignPDF(item)
        PDFManager.SendPDF(signedCopy, client)
        MessageBox.Show("Document signed and successfully delivered to " & client.Username, "Delivered", MessageBoxButton.OK, MessageBoxImage.Information)
    End Sub

    Private Sub SignAndSave_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
        Dim sfd As New SaveFileDialog
        sfd.Filter = "PDF files (*.pdf)|*.pdf"
        Dim res = sfd.ShowDialog
        If res.HasValue AndAlso res.Value = True Then
            Dim item = DirectCast(dgMyDocuments.SelectedItem, PDFItem)
            Dim fileName = sfd.FileName
            Dim signedBytes = PDFManager.SignPDF(item.PDFBytes)
            File.WriteAllBytes(fileName, signedBytes)

            Dim res2 = MessageBox.Show("File was saved successfully. Do you want to open it in your default PDF viewer?", "File saved, open it?", MessageBoxButton.YesNo, MessageBoxImage.Question)
            If res2 = MessageBoxResult.Yes Then
                Diagnostics.Process.Start(fileName)
            End If
        End If
    End Sub

    Private Sub EncryptAndSend_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
        Dim item = DirectCast(dgMyDocuments.SelectedItem, PDFItem)
        Dim client = DirectCast(dgClients.SelectedItem, DiscoveredClientItem)

        Dim recipientCertPair = CertificatesManager.GetUserCertificate(client.Username, SharedTools.CertificateType.Cryptography)
        Select Case recipientCertPair.Item1
            Case SharedTools.CertificateDownloadResponse.Ok
                If recipientCertPair.Item2.NotAfter < DateTime.Now Then
                    Dim res = MessageBox.Show("Warning! The current cryptography certificate in use by " & client.Username & " is expired! You can still use it to encrypt the document, but be aware that its validity cannot be guaranteed by the CA." & vbCrLf & vbCrLf & "Proceed anyway?", "Expired certificate", MessageBoxButton.YesNo, MessageBoxImage.Warning)
                    If res <> MessageBoxResult.Yes Then
                        Return
                    End If
                End If
                Dim encryptedCopy = PDFManager.EncryptPDF(item, recipientCertPair.Item2, If(radioAES128.IsChecked, PdfWriter.ENCRYPTION_AES_128, PdfWriter.ENCRYPTION_AES_256))
                PDFManager.SendPDF(encryptedCopy, client)
                MessageBox.Show("Document encrypted and successfully delivered to " & client.Username, "Delivered", MessageBoxButton.OK, MessageBoxImage.Information)
            Case SharedTools.CertificateDownloadResponse.TargetUserUnknownOrMissingCertificate
                MessageBox.Show("The recipient does not have a valid cryptography certificate registered with the CA. Thus, it wasn't possible to download his cryptography certificate to encrypt the document. Transmission was canceled.", "Certificate missing", MessageBoxButton.OK, MessageBoxImage.Warning)
            Case SharedTools.CertificateDownloadResponse.SignatureException
                PresentationUtilities.ShowStandardMessage(StandardMessages.CASignatureInvalid)
            Case SharedTools.CertificateDownloadResponse.ClientUnknownException
                PresentationUtilities.ShowStandardMessage(StandardMessages.ClientUnknown)
        End Select
    End Sub

    Private Sub SignEncryptAndSend_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
        Dim item = DirectCast(dgMyDocuments.SelectedItem, PDFItem)
        Dim client = DirectCast(dgClients.SelectedItem, DiscoveredClientItem)

        Dim recipientCertPair = CertificatesManager.GetUserCertificate(client.Username, SharedTools.CertificateType.Cryptography)
        Select Case recipientCertPair.Item1
            Case SharedTools.CertificateDownloadResponse.Ok
                If recipientCertPair.Item2.NotAfter < DateTime.Now Then
                    Dim res = MessageBox.Show("Warning! The current cryptography certificate in use by " & client.Username & " is expired! You can still use it to encrypt the document, but be aware that its validity cannot be guaranteed by the CA." & vbCrLf & vbCrLf & "Proceed anyway?", "Expired certificate", MessageBoxButton.YesNo, MessageBoxImage.Warning)
                    If res <> MessageBoxResult.Yes Then
                        Return
                    End If
                End If
                Dim encryptedCopy = PDFManager.SignAndEncryptPDF(item, recipientCertPair.Item2, If(radioAES128.IsChecked, PdfWriter.ENCRYPTION_AES_128, PdfWriter.ENCRYPTION_AES_256))
                PDFManager.SendPDF(encryptedCopy, client)
                MessageBox.Show("Document encrypted and successfully delivered to " & client.Username, "Delivered", MessageBoxButton.OK, MessageBoxImage.Information)
            Case SharedTools.CertificateDownloadResponse.TargetUserUnknownOrMissingCertificate
                MessageBox.Show("The recipient does not have a valid cryptography certificate registered with the CA. Thus, it wasn't possible to download his cryptography certificate to encrypt the document. Transmission was canceled.", "Certificate missing", MessageBoxButton.OK, MessageBoxImage.Warning)
            Case SharedTools.CertificateDownloadResponse.SignatureException
                PresentationUtilities.ShowStandardMessage(StandardMessages.CASignatureInvalid)
            Case SharedTools.CertificateDownloadResponse.ClientUnknownException
                PresentationUtilities.ShowStandardMessage(StandardMessages.ClientUnknown)
        End Select

    End Sub

    Private Sub ReceivedDocuments_SelectionChanged(sender As System.Object, e As System.Windows.Controls.SelectionChangedEventArgs)
        OnPropertyChanged("IsDocumentSelected")
        OnPropertyChanged("IsSignedDocumentSelected")
        OnPropertyChanged("IsEncryptedDocumentSelected")
    End Sub



    Private Sub ViewDocument_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
        Dim item = DirectCast(dgDocuments.SelectedItem, PDFItem)
        Dim toWrite = item.PDFBytes
        If item.IsEncrypted Then
            toWrite = PDFManager.DecryptPDF(toWrite, Repository.CurrentProfile.CryptoCert.Certificate, DirectCast(Repository.CurrentProfile.CryptoCert.CertificateKey(Repository.CurrentPassword).Private, RsaPrivateCrtKeyParameters))
            If toWrite Is Nothing Then
                Dim res = MessageBox.Show("It seems this file was not encrypted with your current certificate. Try to use the old certificate to decrypt?", "Certificate mismatch", MessageBoxButton.YesNo, MessageBoxImage.Question)
                If res = MessageBoxResult.Yes Then
                    Dim oldcerts = CertificatesManager.GetOldCertificates
                    toWrite = PDFManager.DecryptPDF(item, oldcerts.Item1, oldcerts.Item2).PDFBytes
                    If toWrite Is Nothing Then
                        MessageBox.Show("Error! Couldn't decrypt this document with either your current certificate or one of your old certificates.", "Couldn't decrypt", MessageBoxButton.OK, MessageBoxImage.Error)
                        Return
                    End If
                Else
                    Return
                End If
            End If
        End If
        Dim filename = Path.GetTempFileName & ".pdf"
        File.WriteAllBytes(filename, toWrite)
        Diagnostics.Process.Start(filename)

    End Sub

    Private Sub SaveDocument_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
        Dim sfd As New SaveFileDialog
        sfd.Filter = "PDF files (*.pdf)|*.pdf"
        Dim res = sfd.ShowDialog
        If res.HasValue AndAlso res.Value = True Then

            Dim item = DirectCast(dgDocuments.SelectedItem, PDFItem)
            Dim toWrite = item.PDFBytes
            If item.IsEncrypted AndAlso sender Is btnSaveDecrypted Then
                toWrite = PDFManager.DecryptPDF(toWrite, Repository.CurrentProfile.CryptoCert.Certificate, DirectCast(Repository.CurrentProfile.CryptoCert.CertificateKey(Repository.CurrentPassword).Private, RsaPrivateCrtKeyParameters))
                If toWrite Is Nothing Then
                    Dim r = MessageBox.Show("It seems this file was not encrypted with your current certificate. Try to use the old certificate to decrypt?", "Certificate mismatch", MessageBoxButton.YesNo, MessageBoxImage.Question)
                    If r = MessageBoxResult.Yes Then
                        Dim oldcerts = CertificatesManager.GetOldCertificates
                        toWrite = PDFManager.DecryptPDF(item, oldcerts.Item1, oldcerts.Item2).PDFBytes
                        If toWrite Is Nothing Then
                            MessageBox.Show("Error! Couldn't decrypt this document with either your current certificate or one of your old certificates.", "Couldn't decrypt", MessageBoxButton.OK, MessageBoxImage.Error)
                            Return
                        End If
                    Else
                        Return
                    End If
                End If
            End If
            File.WriteAllBytes(sfd.FileName, toWrite)
            Dim res2 = MessageBox.Show("File was saved successfully. Do you want to open it in your default PDF viewer?", "File saved, open it?", MessageBoxButton.YesNo, MessageBoxImage.Question)
            If res2 = MessageBoxResult.Yes Then
                Diagnostics.Process.Start(sfd.FileName)
            End If
        End If
    End Sub

    Private Sub CheckSignature_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
        Dim item = DirectCast(dgDocuments.SelectedItem, PDFItem)
        Dim cert As X509Certificate = Nothing
        Dim prvKey As RsaPrivateCrtKeyParameters = Nothing
        If item.IsEncrypted Then
            cert = Repository.CurrentProfile.CryptoCert.Certificate
            prvKey = DirectCast(Repository.CurrentProfile.CryptoCert.CertificateKey(Repository.CurrentPassword).Private, RsaPrivateCrtKeyParameters)
        End If

        Dim res = PDFManager.VerifyPDFSignature(item.PDFBytes, cert, prvKey)
        Select Case res
            Case PDFVerificationResponse.Valid
                MessageBox.Show("Success. Signature is intact, document has not been altered", "Result", MessageBoxButton.OK, MessageBoxImage.Information)
            Case PDFVerificationResponse.Modified
                MessageBox.Show("Verification failed! Signature is invalid, the document has been altered!", "Result", MessageBoxButton.OK, MessageBoxImage.Warning)
            Case PDFVerificationResponse.CertificateExpiredBeforeSigning
                MessageBox.Show("Warning. Signature is intact, but the document was signed with an expired certificate!", "Result", MessageBoxButton.OK, MessageBoxImage.Warning)
            Case PDFVerificationResponse.CertificateRevokedBeforeSigning
                MessageBox.Show("Warning. Signature is intact, but the document was signed with a REVOKED certificate!", "Result", MessageBoxButton.OK, MessageBoxImage.Warning)
        End Select
    End Sub

    Private Sub Send_Click(sender As System.Object, e As System.Windows.RoutedEventArgs)
        If cbEncrypt.IsChecked AndAlso radioAES256.IsChecked Then
            MessageBox.Show("Sorry, AES_256 encryption is not supported due to a limitation in the iTextSharp library, please choose 128bit encryption instead", "Not supported", MessageBoxButton.OK, MessageBoxImage.Warning)
            Return
        End If

        Try
            If cbSign.IsChecked AndAlso cbEncrypt.IsChecked Then
                SignEncryptAndSend_Click(sender, e)
                Return
            End If

            If cbSign.IsChecked Then
                SignAndSend_Click(sender, e)
                Return
            End If
            If cbEncrypt.IsChecked Then
                EncryptAndSend_Click(sender, e)
                Return
            End If

            Dim item = DirectCast(dgMyDocuments.SelectedItem, PDFItem)
            Dim client = DirectCast(dgClients.SelectedItem, DiscoveredClientItem)
            PDFManager.SendPDF(item, client)

        Catch ex As ServiceModel.CommunicationException
            MessageBox.Show("Communication exception! The client you are trying to contact either went offline or a network problem occourred. Try to re-scan online clients and retry.", "Communication error", MessageBoxButton.OK, MessageBoxImage.Error)
            Client2ClientHelper.FoundClients.Clear()
        End Try
    End Sub

    Public Sub CertificatesUpdated()
        If CanEncrypt = False Then
            cbEncrypt.IsChecked = False
        End If
        If CanSign = False Then
            cbSign.IsChecked = False
        End If

        OnPropertyChanged("CanEncrypt")
        OnPropertyChanged("CanSign")
    End Sub
End Class
